#Setup
#install.packages("tidyverse")
#install.packages("PerformanceAnalytics")
#install.packages("ggfortify")
#install.packages("fastDummies")
library(tidyverse) # core package includes following packages: tidyr, dplyr, ggplot2, readr, purrr, tibble, stringr, forcats
library(plotly)
library("PerformanceAnalytics") #for correlation
library(broom) # for model quantification
library(ggfortify) # for visualizing model fits
library(fastDummies)
library(dplyr)
library(ggplot2)
library(tidyverse)
library(dplyr)
library(tidyr)
library(ggplot2)
library(magrittr)
#install.packages("PerformanceAnalytics")
library("PerformanceAnalytics")
#install.packages("scales")
#library(scales)
Data Wrangling
bike_data <- read_csv("SeoulBikeData.csv",
show_col_types = FALSE,
col_types = cols(Date = col_date(format = "%d/%m/%Y"),
Seasons = col_factor(levels = c("Winter", "Spring", "Summer", "Autumn"),
ordered = TRUE),
Holiday = col_factor(),
"Functioning Day" = col_factor()
))
bike_data <- bike_data %>%
mutate(day = weekdays(Date),
month = months(Date),
day_time = case_when(
Hour >= 5 & Hour < 11 ~ "Morning",
Hour >= 11 & Hour < 15 ~ "Noon",
Hour >= 15 & Hour < 18 ~ "Afternoon",
Hour >= 18 & Hour < 22 ~ "Evening",
Hour < 5 | Hour >= 22 ~ "Night")) %>%
select(Date,month,day,Hour,day_time, Holiday, 'Rented Bike Count',everything())
bike_data$day_time <- factor(bike_data$day_time,
levels = c("Morning", "Noon", "Afternoon", "Evening", "Night"),ordered = TRUE)
bike_data$day <- factor(bike_data$day,
levels = c("Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag" ))
bike_data$month <- factor(bike_data$month,
levels = c("Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober","November", "Dezember"))
#LO1: Performance ## Grafik 1
grafik_1 <- bike_data %>%
plot_ly(x = ~Seasons) %>%
add_histogram(color = I("darkgreen"), opacity = 0.9) %>%
layout(title = "Total bike count by seasons")
Zeit messen bei Grafik 1
start <- Sys.time()
grafik_1
end <- Sys.time()
print(end - start)
Time difference of 0.409903 secs
Grafik 2
grafik_2 <- ggplot(bike_data, aes(`Temperature` ,`Rented Bike Count`, color = `Seasons`))+
geom_jitter(alpha = 0.3)+
scale_fill_grey(start = 0.2, end = 0.8,na.value = "red")+
stat_smooth(method = lm, se = FALSE, color = "red")+
labs(
x = "Temperature in Celsius",
y = "Rented Bikes",
title = "Correlation between temperature and rented bikes")+
theme_minimal()
Zeit messen bei Grafik 2
start <- Sys.time()
grafik_2

end <- Sys.time()
print(end - start)
Time difference of 1.286342 secs
#LO2: Dashboard design principes ## Grafik 3:
grafik_3 <- bike_data %>%
plot_ly(x = ~Seasons) %>%
add_histogram(color = I("navy"), opacity = 0.9) %>%
layout(title = "Total bike count by seasons")
grafik_3
##Grafik 4:
grafik_4 <- ggplot(bike_data, aes(`Temperature` ,`Rented Bike Count`, color = `Seasons`, colors = "Dark2"))+
geom_jitter(alpha = 0.3)+
scale_fill_grey(start = 0.2, end = 0.8,na.value = "red")+
stat_smooth(method = lm, se = FALSE, color = "black")+
labs(
x = "Temperature in Celsius",
y = "Rented Bikes",
title = "Correlation between temperature and rented bikes")+
theme_minimal()
ggplotly(grafik_4)
`geom_smooth()` using formula 'y ~ x'
##Grafik 5
grafik_5 <- ggplot(bike_data,aes(x= Snowfall, y = `Rented Bike Count`))+
geom_jitter(shape=8, (aes(color = Temperature)))+
scale_color_gradient(low="dark blue", high= "light blue")+
facet_wrap(~month)+
xlab("Schnee in Zentimetern") +
ylab("Ausgeliehene Fahrräder")+
ggtitle("Anzahl ausgeliehene Fahrräder und der Einfluss von Schnee")+
theme_minimal()
grafik_5

ggplotly(grafik_5)
Zoomen und Slider: Schlechtes Beispiel
grafik_6 <- ggplot(bike_data, aes(Hour, `Rented Bike Count`))+
geom_point(aes(color = day), alpha = 0.5) +
geom_smooth(aes(color = day, fill = day), method = "lm")+
xlab("Uhrzeit") +
ylab("Anzahl ausgeliehener Fahrräder")+
theme_minimal()
grafik_6

ggplotly(grafik_6, dynamicTicks = TRUE) %>%
rangeslider() %>%
layout(hovermode = "x")
`geom_smooth()` using formula 'y ~ x'
Zoomen und Slider: Gutes Beispiel
# Schritt 1: Übersichtliche Grafik machen mit Stunde und ausgeliehenen Fahrrädern nach Wochentag aufgeteilt.
library(RColorBrewer)
display.brewer.all(colorblindFriendly = TRUE)

grafik_8 <- bike_data %>%
ggplot(aes(Hour, `Rented Bike Count`, color = day)) +
geom_smooth(se = F, size = 2) +
xlab("Uhrzeit") +
ylab("Anzahl ausgeliehener Fahrräder")+
scale_color_discrete("")+
scale_color_brewer(palette = "Paired")+
theme_minimal()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing
scale.
grafik_8

#Schritt 2: Den Slider hinzufügen
ggplotly(grafik_8, dynamicTicks = TRUE) %>%
rangeslider() %>%
layout(hovermode = "x", title = "Der Trend von Fahrradausleihen zu verschiedenen Zeiten und an Wochentagen")
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
#LO3: HCl basics
# Einfacher Linienplot zum Regen in Seoul
ggplot(bike_data) +
geom_line(aes(x=Date,y=Snowfall),color="deepskyblue")+
geom_line(aes(x=Date, y=Rainfall), color="navy")+
labs(title="Täglich gemessener Schneefall und Regen über ein Jahr")+
xlab(label = "Datum")+ ylab(label = " ")+
scale_x_date(date_breaks="months",date_labels="%Y-%m")+
theme_minimal()

rslocator <- function(n=512, type="p", ...)
{
on.exit(return(list(x=x,y=y))) # output even when function is canceled with ESC in console
x <- y <- NULL
i <- 1
while(i<=n)
{
d <- locator(1)
if(is.null(d)) break # If user pressed ESC in Rstudio Graphics window
x <- c(x, d$x)
y <- c(y, d$y)
points(x,y, type=type, ...)
i <- i+1
}
}
#LO4: Evaluation
LS0tDQp0aXRsZTogIklWSSBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojU2V0dXANCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQojaW5zdGFsbC5wYWNrYWdlcygiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnZm9ydGlmeSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZmFzdER1bW1pZXMiKQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBjb3JlIHBhY2thZ2UgaW5jbHVkZXMgZm9sbG93aW5nIHBhY2thZ2VzOiB0aWR5ciwgZHBseXIsIGdncGxvdDIsIHJlYWRyLCBwdXJyciwgdGliYmxlLCBzdHJpbmdyLCBmb3JjYXRzDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikgI2ZvciBjb3JyZWxhdGlvbg0KbGlicmFyeShicm9vbSkgIyBmb3IgbW9kZWwgcXVhbnRpZmljYXRpb24NCmxpYnJhcnkoZ2dmb3J0aWZ5KSAjIGZvciB2aXN1YWxpemluZyBtb2RlbCBmaXRzDQpsaWJyYXJ5KGZhc3REdW1taWVzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KI2luc3RhbGwucGFja2FnZXMoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCmxpYnJhcnkoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJzY2FsZXMiKQ0KI2xpYnJhcnkoc2NhbGVzKQ0KYGBgDQoNCiMjIyBEYXRhIFdyYW5nbGluZw0KYGBge3J9DQpiaWtlX2RhdGEgPC0gcmVhZF9jc3YoIlNlb3VsQmlrZURhdGEuY3N2IiwNCiAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSwNCiAgY29sX3R5cGVzID0gY29scyhEYXRlID0gY29sX2RhdGUoZm9ybWF0ID0gIiVkLyVtLyVZIiksDQogICAgU2Vhc29ucyA9IGNvbF9mYWN0b3IobGV2ZWxzID0gYygiV2ludGVyIiwgIlNwcmluZyIsICJTdW1tZXIiLCAiQXV0dW1uIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpLA0KICAgIEhvbGlkYXkgPSBjb2xfZmFjdG9yKCksDQogICAgIkZ1bmN0aW9uaW5nIERheSIgPSBjb2xfZmFjdG9yKCkNCiAgKSkNCmBgYA0KDQpgYGB7cn0NCmJpa2VfZGF0YSA8LSBiaWtlX2RhdGEgJT4lDQogIG11dGF0ZShkYXkgPSB3ZWVrZGF5cyhEYXRlKSwgDQogICAgICAgICBtb250aCA9IG1vbnRocyhEYXRlKSwNCiAgICAgICAgIGRheV90aW1lID0gY2FzZV93aGVuKA0KICAgICAgICAgICBIb3VyID49IDUgJiBIb3VyIDwgMTEgfiAiTW9ybmluZyIsDQogICAgICAgICAgIEhvdXIgPj0gMTEgJiBIb3VyIDwgMTUgfiAiTm9vbiIsDQogICAgICAgICAgIEhvdXIgPj0gMTUgJiBIb3VyIDwgMTggfiAiQWZ0ZXJub29uIiwNCiAgICAgICAgICAgSG91ciA+PSAxOCAmIEhvdXIgPCAyMiB+ICJFdmVuaW5nIiwNCiAgICAgICAgICAgSG91ciA8IDUgfCBIb3VyID49IDIyIH4gIk5pZ2h0IikpICU+JQ0KICBzZWxlY3QoRGF0ZSxtb250aCxkYXksSG91cixkYXlfdGltZSwgSG9saWRheSwgJ1JlbnRlZCBCaWtlIENvdW50JyxldmVyeXRoaW5nKCkpDQpgYGANCg0KYGBge3J9DQpiaWtlX2RhdGEkZGF5X3RpbWUgPC0gZmFjdG9yKGJpa2VfZGF0YSRkYXlfdGltZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTW9ybmluZyIsICJOb29uIiwgIkFmdGVybm9vbiIsICJFdmVuaW5nIiwgIk5pZ2h0Iiksb3JkZXJlZCA9IFRSVUUpDQoNCmJpa2VfZGF0YSRkYXkgPC0gZmFjdG9yKGJpa2VfZGF0YSRkYXksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTW9udGFnIiwgIkRpZW5zdGFnIiwgIk1pdHR3b2NoIiwgIkRvbm5lcnN0YWciLCAiRnJlaXRhZyIsICJTYW1zdGFnIiwgIlNvbm50YWciICkpDQoNCmJpa2VfZGF0YSRtb250aCA8LSBmYWN0b3IoYmlrZV9kYXRhJG1vbnRoLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiSmFudWFyIiwgIkZlYnJ1YXIiLCAiTcOkcnoiLCAiQXByaWwiLCAiTWFpIiwgIkp1bmkiLCAiSnVsaSIsICJBdWd1c3QiLCAiU2VwdGVtYmVyIiwgIk9rdG9iZXIiLCJOb3ZlbWJlciIsICJEZXplbWJlciIpKQ0KDQpgYGANCg0KI0xPMTogUGVyZm9ybWFuY2UNCiMjIEdyYWZpayAxDQpgYGB7cn0NCmdyYWZpa18xIDwtIGJpa2VfZGF0YSAlPiUNCiAgcGxvdF9seSh4ID0gflNlYXNvbnMpICU+JQ0KCWFkZF9oaXN0b2dyYW0oY29sb3IgPSBJKCJkYXJrZ3JlZW4iKSwgb3BhY2l0eSA9IDAuOSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICJUb3RhbCBiaWtlIGNvdW50IGJ5IHNlYXNvbnMiKQ0KYGBgDQojIyBaZWl0IG1lc3NlbiBiZWkgR3JhZmlrIDENCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KZ3JhZmlrXzENCmVuZCA8LSBTeXMudGltZSgpDQpwcmludChlbmQgLSBzdGFydCkNCmBgYA0KIyMgR3JhZmlrIDINCmBgYHtyfQ0KZ3JhZmlrXzIgPC0gZ2dwbG90KGJpa2VfZGF0YSwgYWVzKGBUZW1wZXJhdHVyZWAgLGBSZW50ZWQgQmlrZSBDb3VudGAsIGNvbG9yID0gYFNlYXNvbnNgKSkrDQogIGdlb21faml0dGVyKGFscGhhID0gMC4zKSsNCiAgc2NhbGVfZmlsbF9ncmV5KHN0YXJ0ID0gMC4yLCBlbmQgPSAwLjgsbmEudmFsdWUgPSAicmVkIikrDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiKSsNCiAgbGFicygNCiAgICB4ID0gIlRlbXBlcmF0dXJlIGluIENlbHNpdXMiLA0KICAgIHkgPSAiUmVudGVkIEJpa2VzIiwNCiAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCByZW50ZWQgYmlrZXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCiMjIFplaXQgbWVzc2VuIGJlaSBHcmFmaWsgMg0KYGBge3J9DQpzdGFydCA8LSBTeXMudGltZSgpDQpncmFmaWtfMg0KZW5kIDwtIFN5cy50aW1lKCkNCnByaW50KGVuZCAtIHN0YXJ0KQ0KYGBgDQojTE8yOiBEYXNoYm9hcmQgZGVzaWduIHByaW5jaXBlcw0KIyMgR3JhZmlrIDM6IA0KYGBge3J9DQpncmFmaWtfMyA8LSBiaWtlX2RhdGEgJT4lDQogIHBsb3RfbHkoeCA9IH5TZWFzb25zKSAlPiUNCglhZGRfaGlzdG9ncmFtKGNvbG9yID0gSSgibmF2eSIpLCBvcGFjaXR5ID0gMC45KSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gIlRvdGFsIGJpa2UgY291bnQgYnkgc2Vhc29ucyIpDQoNCmdyYWZpa18zDQpgYGANCg0KIyNHcmFmaWsgNDogDQpgYGB7cn0NCmdyYWZpa180IDwtIGdncGxvdChiaWtlX2RhdGEsIGFlcyhgVGVtcGVyYXR1cmVgICxgUmVudGVkIEJpa2UgQ291bnRgLCBjb2xvciA9IGBTZWFzb25zYCwgY29sb3JzID0gIkRhcmsyIikpKw0KICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMykrDQogIHNjYWxlX2ZpbGxfZ3JleShzdGFydCA9IDAuMiwgZW5kID0gMC44LG5hLnZhbHVlID0gInJlZCIpKw0KICBzdGF0X3Ntb290aChtZXRob2QgPSBsbSwgc2UgPSBGQUxTRSwgY29sb3IgPSAiYmxhY2siKSsNCiAgbGFicygNCiAgICB4ID0gIlRlbXBlcmF0dXJlIGluIENlbHNpdXMiLA0KICAgIHkgPSAiUmVudGVkIEJpa2VzIiwNCiAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIHRlbXBlcmF0dXJlIGFuZCByZW50ZWQgYmlrZXMiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3RseShncmFmaWtfNCkNCmBgYA0KIyNHcmFmaWsgNQ0KYGBge3J9DQpncmFmaWtfNSA8LSBnZ3Bsb3QoYmlrZV9kYXRhLGFlcyh4PSBTbm93ZmFsbCwgeSA9IGBSZW50ZWQgQmlrZSBDb3VudGApKSsNCiAgZ2VvbV9qaXR0ZXIoc2hhcGU9OCwgKGFlcyhjb2xvciA9IFRlbXBlcmF0dXJlKSkpKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9ImRhcmsgYmx1ZSIsIGhpZ2g9ICJsaWdodCBibHVlIikrDQogIGZhY2V0X3dyYXAofm1vbnRoKSsNCiAgeGxhYigiU2NobmVlIGluIFplbnRpbWV0ZXJuIikgKw0KICB5bGFiKCJBdXNnZWxpZWhlbmUgRmFocnLDpGRlciIpKw0KICBnZ3RpdGxlKCJBbnphaGwgYXVzZ2VsaWVoZW5lIEZhaHJyw6RkZXIgdW5kIGRlciBFaW5mbHVzcyB2b24gU2NobmVlIikrDQogIHRoZW1lX21pbmltYWwoKQ0KDQpncmFmaWtfNQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90bHkoZ3JhZmlrXzUpDQpgYGANCg0KIyMgWm9vbWVuIHVuZCBTbGlkZXI6IFNjaGxlY2h0ZXMgQmVpc3BpZWwNCmBgYHtyfQ0KZ3JhZmlrXzYgPC0gZ2dwbG90KGJpa2VfZGF0YSwgYWVzKEhvdXIsIGBSZW50ZWQgQmlrZSBDb3VudGApKSsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkYXkpLCBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBkYXksIGZpbGwgPSBkYXkpLCBtZXRob2QgPSAibG0iKSsNCiAgeGxhYigiVWhyemVpdCIpICsNCiAgeWxhYigiQW56YWhsIGF1c2dlbGllaGVuZXIgRmFocnLDpGRlciIpKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ3JhZmlrXzYNCmBgYA0KDQpgYGB7cn0NCmdncGxvdGx5KGdyYWZpa182LCBkeW5hbWljVGlja3MgPSBUUlVFKSAlPiUNCiAgcmFuZ2VzbGlkZXIoKSAlPiUNCiAgbGF5b3V0KGhvdmVybW9kZSA9ICJ4IikNCmBgYA0KIyMgWm9vbWVuIHVuZCBTbGlkZXI6IEd1dGVzIEJlaXNwaWVsDQpgYGB7cn0NCiMgU2Nocml0dCAxOiDDnGJlcnNpY2h0bGljaGUgR3JhZmlrIG1hY2hlbiBtaXQgU3R1bmRlIHVuZCBhdXNnZWxpZWhlbmVuIEZhaHJyw6RkZXJuIG5hY2ggV29jaGVudGFnIGF1ZmdldGVpbHQuIA0KDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmRpc3BsYXkuYnJld2VyLmFsbChjb2xvcmJsaW5kRnJpZW5kbHkgPSBUUlVFKQ0KDQpncmFmaWtfOCA8LSBiaWtlX2RhdGEgJT4lIA0KICBnZ3Bsb3QoYWVzKEhvdXIsIGBSZW50ZWQgQmlrZSBDb3VudGAsIGNvbG9yID0gZGF5KSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEYsIHNpemUgPSAyKSArDQogIHhsYWIoIlVocnplaXQiKSArDQogIHlsYWIoIkFuemFobCBhdXNnZWxpZWhlbmVyIEZhaHJyw6RkZXIiKSsNCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoIiIpKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJQYWlyZWQiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdyYWZpa184DQpgYGANCmBgYHtyfQ0KI1NjaHJpdHQgMjogRGVuIFNsaWRlciBoaW56dWbDvGdlbg0KZ2dwbG90bHkoZ3JhZmlrXzgsIGR5bmFtaWNUaWNrcyA9IFRSVUUpICU+JQ0KICByYW5nZXNsaWRlcigpICU+JQ0KICBsYXlvdXQoaG92ZXJtb2RlID0gIngiLCB0aXRsZSA9ICJEZXIgVHJlbmQgdm9uIEZhaHJyYWRhdXNsZWloZW4genUgdmVyc2NoaWVkZW5lbiBaZWl0ZW4gdW5kIGFuIFdvY2hlbnRhZ2VuIikNCmBgYA0KDQojTE8zOiBIQ2wgYmFzaWNzDQpgYGB7cn0NCiMgRWluZmFjaGVyIExpbmllbnBsb3QgenVtIFJlZ2VuIGluIFNlb3VsDQoNCmdncGxvdChiaWtlX2RhdGEpICsNCiAgZ2VvbV9saW5lKGFlcyh4PURhdGUseT1Tbm93ZmFsbCksY29sb3I9ImRlZXBza3libHVlIikrDQogIGdlb21fbGluZShhZXMoeD1EYXRlLCB5PVJhaW5mYWxsKSwgY29sb3I9Im5hdnkiKSsNCiAgbGFicyh0aXRsZT0iVMOkZ2xpY2ggZ2VtZXNzZW5lciBTY2huZWVmYWxsIHVuZCBSZWdlbiDDvGJlciBlaW4gSmFociIpKw0KICB4bGFiKGxhYmVsID0gIkRhdHVtIikrIHlsYWIobGFiZWwgPSAiICIpKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3M9Im1vbnRocyIsZGF0ZV9sYWJlbHM9IiVZLSVtIikrDQogIHRoZW1lX21pbmltYWwoKQ0KDQpyc2xvY2F0b3IgPC0gZnVuY3Rpb24obj01MTIsIHR5cGU9InAiLCAuLi4pDQogew0KIG9uLmV4aXQocmV0dXJuKGxpc3QoeD14LHk9eSkpKSAjIG91dHB1dCBldmVuIHdoZW4gZnVuY3Rpb24gaXMgY2FuY2VsZWQgd2l0aCBFU0MgaW4gY29uc29sZQ0KIHggPC0geSA8LSBOVUxMDQogaSA8LSAxDQogd2hpbGUoaTw9bikNCiAgIHsNCiAgIGQgPC0gbG9jYXRvcigxKQ0KICAgaWYoaXMubnVsbChkKSkgYnJlYWsgIyBJZiB1c2VyIHByZXNzZWQgRVNDIGluIFJzdHVkaW8gR3JhcGhpY3Mgd2luZG93DQogICB4IDwtIGMoeCwgZCR4KQ0KICAgeSA8LSBjKHksIGQkeSkNCiAgIHBvaW50cyh4LHksIHR5cGU9dHlwZSwgLi4uKQ0KICAgaSA8LSBpKzENCiAgIH0NCiB9DQpgYGANCg0KI0xPNDogRXZhbHVhdGlvbg0K